package java_thinking.unit_15._15_10;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description : 通配符的演变
 * @author: cww
 * @DateTime: 2019-04-05 21:57
 */

class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
/**
 * 使用泛型容器来代替数组，见下面的案例无法通过编译
 */
public class NonCovariantGenerics {
    //编译无法通过，关键这不是向上转型，归根到底Apple的List不是Fruit的List，前者的类型不等价与后者
    //可以理解为这已经是容器的类型问题了，而不是容器持有的类型，
    //容器的类型Fruit与容器的类型Apple当然不是一个东西
    //! List<Fruit> flist = new ArrayList<Apple>();
}

/**
 * 真正的问题是我们在谈论容器的类型，而不是容器持有的类型。
 * 与数组不同，泛型没有内建的协变类型，这是因为数组在语言中是完全定义的，因此可以
 * 内建了编译期和运行时的检查，但是使用泛型时，编译器和运行系统都不知道你想用什么类型，
 * 以及他要采用什么样的规则。
 * 但是有时想要在两个类型之间建立某种类型的向上转型关系，这是通配符所允许的
 *
 * 通配符引用的是明确的类型，因此它意味着"某种fruits引用么有指定具体类型"
 *
 * extends确定了泛型的上界，而super确定了泛型的下界
 */
class GenericsAndCovariance {
    public static void main(String[] args) {
        //可以读作：具有任何从Fruit继承的类型的列表，但是这并不意味着这个List将持有任何类型的Fruit。
        //但是无法将Fruit的任何子类放入这个fruits的!!!!
        List<? extends Fruit> fruits = new ArrayList<Apple>();
        /**
         * 当前无法添加任何类型，因为当我们指定一个ArrayList<? extends Fruit>时，add()参数就变成了
         * “? extends Fruit”，从这个描述中，编译器并不能了解这里需要Fruit的哪个具体类型，因此他不会
         * 接受任何类型的Fruit，即使将Apple向上转型为Fruit也无用，编译器是无法知道"任何事物对象"该选哪个
         */
        //! fruits.add(new Apple())
        //! fruits.add(new Fruit())
        //! fruits.add(new Object())
        fruits.add(null);
        /**
         * 下面两个方法并不是有着编译器上的代码检查，而是这两种方法接收的是Object类型，因此不涉及任何通配符
         */
        fruits.contains(new Apple());
        fruits.indexOf(new Apple());
        /**
         * 这里get也只能返回一个fruit，这就是在给定"任何扩展自Fruit对象"这一边界之后，编译器所能知道的一切信息了
         */
        Fruit fruit = fruits.get(0);
        Fruit f = fruits.get(0);

    }
}
